<?php
namespace app\mxty\controller;

use app\api\model\Wxapp as WxappModel;
use app\common\model\User as UserModel;

use app\mxty\logic\Booking as BookingLogic;
use app\mxty\logic\MemberCard as MemberCardLogic;
use app\mxty\logic\Course as CourseLogic;

use app\common\enum\order\PayStatus as OrderPayStatusEnum;
use app\common\library\helper;

// 微信支付回调类
class Payment
{

    public function __construct()
    {
        $this->config = self::getWxConfig();
        $this->config !== false && $this->setConfig($this->config['app_id'], $this->config['app_secret']);
    }

    protected function setConfig($appId = null, $appSecret = null)
    {
        !empty($appId) && $this->appId = $appId;
        !empty($appSecret) && $this->appSecret = $appSecret;
    }

    /**
     *  预订场地的微信支付回调
     */
    public function bookingNotify()
    {
        if (!$xml = file_get_contents('php://input')) {
            $this->returnCode(false, 'Not found DATA');
        }
        // 将服务器返回的XML数据转化为数组
        $data = $this->fromXml($xml);
        // 记录日志
        log_record($xml);
        log_record($data);
        
        // 查询对应订单信息
        $order = (new BookingLogic())->getOrderByNo($data['out_trade_no']);
        // 订单不存在或订单已支付
        if($order->isEmpty() || $order->pay_status == OrderPayStatusEnum::SUCCESS) {
            $this->returnCode(false, '订单不存在');
        }
        
        // 保存微信服务器返回的签名sign
        $dataSign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        // 生成签名
        $sign = $this->makeSign($data);
        // 判断签名是否正确 判断支付状态
        if (
            ($sign !== $dataSign)
            || ($data['return_code'] !== 'SUCCESS')
            || ($data['result_code'] !== 'SUCCESS')
        ) {
            $this->returnCode(false, '签名失败');
        }
        // 订单支付成功业务处理
        $order->pay_time = time(); // 更新支付时间为当前时间
        $order->pay_status = OrderPayStatusEnum::SUCCESS;
        $order->transaction_id = $data['transaction_id'];
        foreach($order->fragments as $fragment) {
            $fragment->pay_status = OrderPayStatusEnum::SUCCESS;
            $fragment->save();
        }
        $order->save();
        // 累积用户总消费金额
        UserModel::setIncPayMoney($order->user_id, $order->total_price);
        // 返回状态
        $this->returnCode(true, 'OK');
    }

    /**
     *  购买会员卡的微信支付回调
     */
    public function buyCardNotify()
    {
        if (!$xml = file_get_contents('php://input')) {
            $this->returnCode(false, 'Not found DATA');
        }
        // 将服务器返回的XML数据转化为数组
        $data = $this->fromXml($xml);
        // 记录日志
        log_record($xml);
        log_record($data);
        
        // 查询对应订单信息
        $order = MemberCardLogic::getOrderInfo($data['out_trade_no']);
        // 订单不存在或订单已支付
        if($order->isEmpty() || $order->pay_status == OrderPayStatusEnum::SUCCESS) {
            $this->returnCode(false, '订单不存在');
        }
        
        // 保存微信服务器返回的签名sign
        $dataSign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        // 生成签名
        $sign = $this->makeSign($data);
        // 判断签名是否正确 判断支付状态
        if (
            ($sign !== $dataSign)
            || ($data['return_code'] !== 'SUCCESS')
            || ($data['result_code'] !== 'SUCCESS')
        ) {
            $this->returnCode(false, '签名失败');
        }
        // 订单支付成功业务处理
        $order->pay_time = time(); // 更新支付时间为当前时间
        $order->pay_status = OrderPayStatusEnum::SUCCESS;
        $order->transaction_id = $data['transaction_id'];
        $order->save();
        // 累积用户总消费金额
        UserModel::setIncPayMoney($order->user_id, $order->price);
        // 返回状态
        $this->returnCode(true, 'OK');
    }

    /**
     *  购买课程的微信支付回调
     */
    public function buyCourseNotify()
    {
        if (!$xml = file_get_contents('php://input')) {
            $this->returnCode(false, 'Not found DATA');
        }
        // 将服务器返回的XML数据转化为数组
        $data = $this->fromXml($xml);
        // 记录日志
        log_record($xml);
        log_record($data);
        
        // 查询对应订单信息
        $order = CourseLogic::getOrderInfo($data['out_trade_no']);
        // 订单不存在或订单已支付
        if($order->isEmpty() || $order->pay_status == OrderPayStatusEnum::SUCCESS) {
            $this->returnCode(false, '订单不存在');
        }
        
        // 保存微信服务器返回的签名sign
        $dataSign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        // 生成签名
        $sign = $this->makeSign($data);
        // 判断签名是否正确 判断支付状态
        if (
            ($sign !== $dataSign)
            || ($data['return_code'] !== 'SUCCESS')
            || ($data['result_code'] !== 'SUCCESS')
        ) {
            $this->returnCode(false, '签名失败');
        }
        // 订单支付成功业务处理
        $order->pay_time = time(); // 更新支付时间为当前时间
        $order->pay_status = OrderPayStatusEnum::SUCCESS;
        $order->transaction_id = $data['transaction_id'];
        $order->save();
        // 累积用户总消费金额
        UserModel::setIncPayMoney($order->user_id, $order->price);
        // 返回状态
        $this->returnCode(true, 'OK');
    }

    /**
     * 返回状态给微信服务器
     * @param boolean $returnCode
     * @param string $msg
     */
    private function returnCode($returnCode = true, $msg = null)
    {
        // 返回状态
        $return = [
            'return_code' => $returnCode ? 'SUCCESS' : 'FAIL',
            'return_msg' => $msg ?: 'OK',
        ];
        // 记录日志
        log_record([
            'name' => '返回微信支付状态',
            'data' => $return
        ]);
        die($this->toXml($return));
    }

    /**
     * 生成签名
     * @param $values
     * @return string 本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    private function makeSign($values)
    {
        //签名步骤一：按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);
        //签名步骤二：在string后加入KEY
        $string = $string . '&key=' . $this->config['apikey'];
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 输出xml字符
     * @param $values
     * @return bool|string
     */
    private function toXml($values)
    {
        if (!is_array($values)
            || count($values) <= 0
        ) {
            return false;
        }

        $xml = "<xml>";
        foreach ($values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * 将xml转为array
     * @param $xml
     * @return mixed
     */
    private function fromXml($xml)
    {
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return helper::jsonDecode(helper::jsonEncode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)));
    }

    /**
     * 格式化参数格式化成url参数
     * @param $values
     * @return string
     */
    private function toUrlParams($values)
    {
        $buff = '';
        foreach ($values as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }
        return trim($buff, '&');
    }

    /**
     * 获取微信支付配置
     * @return array
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private static function getWxConfig()
    {
        return WxappModel::getWxappCache(getStoreId());
    }
}
